<script setup lang="ts">
import { ref, reactive, computed, useCssModule } from 'vue'
import type { Ref } from 'vue'
// 响应性语法糖，默认是关闭状态，需要在 vite.config.js 中启用
import { $ref } from 'vue/macros'
// 当使用 <script setup> 时，这个 defineProps() 宏函数支持从它的参数中推导类型
// const props = defineProps({
//   msg: {
//     type: String,
//     required: true
//   }
// }) // 运行时声明
// console.log(props.msg)


// 通过泛型参数来定义 prop 的类型通常更直接
// const props = defineProps<{
//   msg: string
// }>() // 基于类型的声明
// console.log(props.msg)
// defineProps<{ msg: string }>()

// 基于类型的声明或者运行时声明都可以使用，但是不能同时使用两者

// 也可以将 prop 的类型移入一个单独的接口中
interface Props {
  msg: string
  age?: number
}
const { msg, age = 18 } = defineProps<Props>() // 设置默认值
console.log(msg, age)

// 在 <script setup>中，emit函数的类型标注可以通过运行时声明或类型声明进行
// 运行时
// const emit = defineEmits(['change'])

// 基于类型
const emit = defineEmits<{
  (e: 'change', val: number): void
}>()

const changeFromParent = () => {
  emit('change', 4)
}

// 为 ref 标注类型，也可以推导出类型 Ref<number>
const year = ref(2000)
// year.value = '222' // error

// 如果我们想为 ref 内的值指定一个更复杂的类型，可以通过使用 Ref 这个类型
const name: Ref<string | number> = ref('2022')
name.value = 2022
// 或者，在调用 ref() 时传入一个泛型参数，来覆盖默认的推导行为
// 得到的类型：Ref<string | number>
const gender = ref<string | number>(1)

// 如果指定了泛型参数但没有给出初始值，最后得到的就是一个包含 undefined 的联合类型
// 推导出的类型：Ref<number | undefined>
const n = ref<number>()


// 为 reactive 标注类型
// 推导得到的类型：{ title: string }
const book = reactive({ title: 'Vue3 reactive' })

// 要显式的标注一个 reactive property 的类型，可以使用接口
interface Fish {
  weight: number
  name: string
}
const fish: Fish = reactive({
  weight: 1,
  name: '乌鱼'
})
// 不推荐使用 reactive() 的泛型参数，因为处理了深层次 ref 解包的返回值与泛型参数的类型不同

const data = ref(0)
// computed() 会从其计算函数的返回值上推导出类型
// 推导得到的类型 computedRef<number>
// const double = computed(() => data.value * 2)
// 也可以通过泛型参数显式指定类型
const double = computed<number>(() => data.value * 2)

const info = $ref(1)

const color = ref('red')
const background = reactive({
  theme: 'green'
})

// 与组合式API一起使用
// 在 setup() 作用域中，默认情况下，返回 <style module> 的 class。
const module = useCssModule()
// 具名情况下，返回 <style module="newClasses"> 的 class
// const module = useCssModule('newClasses')
console.log(module)
</script>

<template>
  <p :class="$style.red">info:{{ info }}</p>
  <p class="world">{{ msg }}</p>

  <button type="button" @click="changeFromParent" class="blue">调用父组件的方法</button>
</template>

<style module>
/* CSS Modules：一个 <style module> 标签会被编译为 CSS　Modules 并且将生成的 css class 作为 $style 对象暴露给组件。得出的class将被哈希化，从而将css仅用于当前组件 */
/* 也可以自定义注入名称，通过给 module attribute 一个值来自定义注入 class 对象的 property 键 <style module="newClasses">  <p :class="newClasses.red">info:{{ info }}</p>*/
.red {
  /* 单文件组件的 <style> 标签支持使用 v-bind css 函数将 css的值链接到动态的组件状态 */
  color: v-bind(color);
  /* 支持javascript表达式（需要用引号包裹起来） */
  background: v-bind('background.theme')
}
</style>
